package codegen;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import codegen.util.database.Field;
import codegen.util.database.Table;

public class CodeGenEngine {
	private Table table;
	private List<Field> fieldList;
	private String tableName;
	private final static String templatePath = System.getProperty("user.dir")
			+ File.separator + "template" + File.separator;
	private String genPath;

	private final static String BEGIN_LOOP = "%begin_loop";
	private final static String END_LOOP = "%end_loop";

//	private static String DTO_PACKAGE = CommonPara.package_prefix + ".model";
//
//	private static String DAO_PACKAGE = CommonPara.package_prefix + ".dao";
//
//	private String SERVICE_PACKAGE = CommonPara.package_prefix + ".service";
//	private String SERVICE_PACKAGE_IMPL = SERVICE_PACKAGE + ".impl";
//	
//	private String CONTROLLER_PACKAGE = CommonPara.package_prefix + ".controller";
//
//	private String PACKAGE_NAME = CommonPara.package_name;
//	
//	private String MYBATIS_PACKAGE = CommonPara.package_prefix + ".config";
	
	private static String DTO_PACKAGE = CommonPara.package_prefix + ".dto";
	
	private static String PO_PACKAGE = CommonPara.package_prefix + ".po";
	
	private static String VO_PACKAGE = CommonPara.package_prefix + ".vo";
	
	private static String TRANSFORM_PACKAGE = CommonPara.package_prefix + ".transform";
	
	private static String VO_TRANSFORM_PACKAGE = CommonPara.package_prefix + ".voTransform";
	
	private static String CONSTANT_PACKAGE = CommonPara.package_prefix + ".constant";

	private static String DAO_PACKAGE = CommonPara.package_prefix + ".dao";

	private String SERVICE_PACKAGE = CommonPara.package_prefix + ".service";
	
	private String SERVICE_PACKAGE_IMPL = SERVICE_PACKAGE + ".impl";
	
	private static String QUERY_CONDITION_PACKAGE = CommonPara.package_prefix + ".condition";
	
	/**
	 * 这个service是独立给WEB的Service使用的
	 * 因为客户端和服务端的包路径相同不能生成一样的类
	 */
	private String WEB_SERVICE_PACKAGE = CommonPara.package_prefix + ".web.service";
	
	/**
	 * 这个service实现类是独立给WEB的Service使用的
	 * 因为客户端和服务端的包路径相同不能生成一样的类
	 */
	private String WEB_SERVICE_PACKAGE_IMPL = WEB_SERVICE_PACKAGE + ".impl";
	
	
	private String CONTROLLER_PACKAGE = CommonPara.package_prefix + ".controller";

	private String PACKAGE_NAME = CommonPara.package_name;
	
	private String MYBATIS_PACKAGE = CommonPara.package_prefix + ".mybatis."+CommonPara.package_name;
	
	//private String MYBATIS_CONFIG_PACKAGE = CommonPara.package_prefix + ".mybatis";

	public void setTable(Table table) {
		this.table = table;
		this.fieldList = this.table.getFieldList();
		this.tableName = table.getTableName();
	}

	public Table getTable() {
		return table;
	}

	public CodeGenEngine() {
	}

	public CodeGenEngine(String genPath) {
		this.genPath = genPath;
	}

	public CodeGenEngine(Table table) {
		this.table = table;
		this.fieldList = table.getFieldList();
		this.tableName = table.getTableName();
	}

	public void genCode() throws IOException, SQLException {

		genController();
		genPo();
		genVo();
		genDto();
		genDao();
		genService();
		genServiceImpl();
		genWebService();
		genWebServiceImpl();
		genTransform();
		genVOTransform();
		genConstant();
		//genMyBatisConfig();
		genSqlMapper();
		genSqlResultMapper();
		genQueryCondition();
	}
	
	private void genController() throws IOException, SQLException {
		this.formFile(templatePath + "ctrl.txt", getPath(CONTROLLER_PACKAGE)
				+ getClassName() + "Controller.java", CommonPara.JAVA_ENCODE);
	}

	private void genConstant() throws IOException, SQLException {
		this.formFile(templatePath + "constant.txt", getPath(CONSTANT_PACKAGE)
				+ getClassName() + "Constant.java", CommonPara.JAVA_ENCODE);
	}
	
	private void genVo() throws IOException, SQLException {
		this.formFile(templatePath + "vo.txt", getPath(VO_PACKAGE)
				+ getClassName() + "VO.java", CommonPara.JAVA_ENCODE);
	}
	
	private void genPo() throws IOException, SQLException {
		this.formFile(templatePath + "po.txt", getPath(PO_PACKAGE)
				+ getClassName() + ".java", CommonPara.JAVA_ENCODE);
	}
	
	private void genTransform() throws IOException, SQLException {
		this.formFile(templatePath + "transform.txt", getPath(TRANSFORM_PACKAGE)
				+ getClassName() + "Transform.java", CommonPara.JAVA_ENCODE);
	}
	
	private void genVOTransform() throws IOException, SQLException {
		this.formFile(templatePath + "voTransform.txt", getPath(VO_TRANSFORM_PACKAGE)
				+ getClassName() + "VOTransform.java", CommonPara.JAVA_ENCODE);
	}
	
	private void genDto() throws IOException, SQLException {
		this.formFile(templatePath + "dto.txt", getPath(DTO_PACKAGE)
				+ getClassName() + "DTO.java", CommonPara.JAVA_ENCODE);
	}

	private void genDao() throws IOException, SQLException {
		this.formFile(templatePath + "dao.txt", getPath(DAO_PACKAGE)
				+ getClassName() + "Mapper.java", CommonPara.JAVA_ENCODE);
	}

	private void genService() throws IOException, SQLException {
		this.formFile(templatePath + "service.txt", getPath(SERVICE_PACKAGE)
				+ "I" + getClassName() + "Service.java", CommonPara.JAVA_ENCODE);
	}

	private void genServiceImpl() throws IOException, SQLException {
		this.formFile(templatePath + "serviceImpl.txt",
				getPath(SERVICE_PACKAGE_IMPL) + getClassName()
						+ "ServiceImpl.java", CommonPara.JAVA_ENCODE);
	}
	
	private void genWebService() throws IOException, SQLException {
		this.formFile(templatePath + "webService.txt", getPath(WEB_SERVICE_PACKAGE)
				+ "I" + getClassName() + "Service.java", CommonPara.JAVA_ENCODE);
	}

	private void genWebServiceImpl() throws IOException, SQLException {
		this.formFile(templatePath + "webServiceImpl.txt",
				getPath(WEB_SERVICE_PACKAGE_IMPL) + getClassName()
						+ "ServiceImpl.java", CommonPara.JAVA_ENCODE);
	}

	private void genSqlMapper() throws IOException, SQLException {
		this.formFile(templatePath + "mybatisSqlMapper.txt",
				getPath(MYBATIS_PACKAGE) + transToMiddleline(getClassName()) + "-mapper.xml",
				CommonPara.XML_ENCODE);
	}
	
	private void genSqlResultMapper() throws IOException, SQLException {
		this.formFile(templatePath + "mybatisSqlResultMapper.txt",
				getPath(MYBATIS_PACKAGE) + transToMiddleline(getClassName()) + "-result-mapper.xml",
				CommonPara.XML_ENCODE);
	}
	
	/*private void genMyBatisConfig() throws IOException, SQLException {
		this.formFile(templatePath + "mybatisConfig.txt",
				getPath(MYBATIS_CONFIG_PACKAGE)+ transToMiddleline(getClassName()) + "-mybatis-config.xml",
				CommonPara.XML_ENCODE);
	}*/
	
	private void genQueryCondition() throws IOException, SQLException {
		this.formFile(templatePath + "queryCondition.txt", getPath(QUERY_CONDITION_PACKAGE)
				+ getClassName() + "QC.java", CommonPara.JAVA_ENCODE);
	}

	private void formFile(String modelFile, String destFilePath, String encode)
			throws IOException, SQLException {
		BufferedReader br = null;
		BufferedWriter bw = null;
		File dir = new File(destFilePath).getParentFile();
		System.out.println(destFilePath);
		dir.mkdirs();
		try {
			OutputStreamWriter osw = new OutputStreamWriter(
					new FileOutputStream(destFilePath), encode);
			InputStreamReader isw = new InputStreamReader(new FileInputStream(
					modelFile), CommonPara.IN_ENCODE);
			br = new BufferedReader(isw);
			bw = new BufferedWriter(osw);
			String s = br.readLine();
			while (s != null) {
				if (s.trim().length() == 0) {
					bw.write(s);
				} else if (s.indexOf(BEGIN_LOOP) >= 0) {
					bw.write(this.replaceStr(this.processLoop(s, br)));
				} else {
					s = this.replaceStr(s);
					bw.write(s);
				}

				bw.newLine();
				s = br.readLine();
			}

		} catch (IOException ex) {
			throw ex;
		} finally {
			if (br != null) {
				br.close();
			}
			if (bw != null) {
				bw.close();

			}
		}
	}

	public String getClassName() {
		return this.table.getClassName();
	}

	private String getPath(String packageName) {
		return genPath + packageName.replace('.', File.separatorChar)
				+ File.separatorChar;
	}

	public String getObjectName() {
		String s = this.tableName.toLowerCase();
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '_') {
				s = s.substring(0, i) + (s.charAt(i + 1) + "").toUpperCase()
						+ s.substring(i + 2);
			}
		}
		return s;
	}

	private String processLoop(String loopString, BufferedReader br) {
		String loopBody = "";
		while (true) {
			String line = null;
			try {
				line = br.readLine();
			} catch (IOException ex) {
			}
			if (line == null) {
				throw new RuntimeException(loopString + "line is null");
			}
			if (loopString == null) {
				throw new RuntimeException("loopString is null");
			}
			if (line.trim()
					.equals(loopString.trim().replaceAll("begin", "end"))) {
				break;
			}

			loopBody += line + "\n";
		}

		loopBody = this.processLoopBody(loopBody, loopString);

		return loopBody;
	}

	private String replaceStr(String s) {
		
		s = s.replaceAll("%ctrl_package%", CONTROLLER_PACKAGE);
		
		s = s.replaceAll("%package_name%", PACKAGE_NAME);

		s = s.replaceAll("%po_package%", PO_PACKAGE);
		
		s = s.replaceAll("%constant_package%", CONSTANT_PACKAGE);
		
		s = s.replaceAll("%transform_package%", TRANSFORM_PACKAGE);
		
		s = s.replaceAll("%vo_package%", VO_PACKAGE);
		
		s = s.replaceAll("%dto_package%", DTO_PACKAGE);
		
		s = s.replaceAll("%query_condition_package%", QUERY_CONDITION_PACKAGE);
		
		s = s.replaceAll("%table_comment%", table.getTableDesc());
		if (s.indexOf("%table_class_name%") >= 0) {
			s = s.replaceAll("%table_class_name%", getClassName());
		}

		s = s.replaceAll("%dao_package%", DAO_PACKAGE);

		if (s.indexOf("%table_object_name%") >= 0) {
			s = s.replaceAll("%table_object_name%",
					this.getObjectName(table.getTableName()));
		}

		s = s.replaceAll("%service_package%", SERVICE_PACKAGE);

		s = s.replaceAll("%serviceimpl_package%", SERVICE_PACKAGE_IMPL);
		
		s = s.replaceAll("%web_service_package%", WEB_SERVICE_PACKAGE);

		s = s.replaceAll("%web_serviceimpl_package%", WEB_SERVICE_PACKAGE_IMPL);
		

		s = s.replaceAll("%single_field_list%",
				table.getSinglefieldListString());
		
		s = s.replaceAll("%insert_field_list%",
				table.getInserfieldListString());
		
		s = s.replaceAll("%insert_values_list%",
				table.getInsertvaluesString());
		
		s = s.replaceAll("%update_field_list%",
				table.getUpdatefieldListString());
		
		s = s.replaceAll("%condition_field_list%",
				table.getConditionfieldliststring());

		if (s.indexOf("%table_name%") >= 0) {
			s = s.replaceAll("%table_name%", tableName);
		}

		return s;
	}

	private String processLoopBody(String loopBody, String loopString) {
		String[] lines = loopBody.split("\n");
		String s = "";
		if (loopString.indexOf("begin_loop_field%") >= 0) {
			for (int i = 0; i < this.fieldList.size(); i++) {
				for (int j = 0; j < lines.length; j++) {
					if (lines[j].indexOf(BEGIN_LOOP) < 0) {
						if (!this.fieldList.get(i).getObjectName().equals("id")
								&& !this.fieldList.get(i).getObjectName()
										.equals("crtUsrId")
								&& !this.fieldList.get(i).getObjectName()
										.equals("crtTm")
								&& !this.fieldList.get(i).getObjectName()
										.equals("mdfUsrId")
								&& !this.fieldList.get(i).getObjectName()
										.equals("mdfTm")) {
							String temp = this.generateModel(lines[j], i);
							s += temp + "\n";
						}
					} else {
						String newLoopString = lines[j];
						String newLoopBody = "";
						while (lines[++j].trim().startsWith(END_LOOP) == false
								|| lines[j].indexOf(newLoopString
										.substring(newLoopString
												.indexOf("begin") + 5)) < 0) {
							newLoopBody += lines[j] + "\n";
						}
						s += this.processLoopBody(newLoopBody, newLoopString);
					}
				}
			}
		}else if(loopString.indexOf("begin_loop_field_get_set%") >= 0){
			if (loopString.indexOf("begin_loop_field_get_set%") >= 0) {
				for (int i = 0; i < this.fieldList.size(); i++) {
					for (int j = 0; j < lines.length; j++) {
						if (lines[j].indexOf(BEGIN_LOOP) < 0) {
							if (!this.fieldList.get(i).getObjectName().equals("id")
									&& !this.fieldList.get(i).getObjectName()
											.equals("crtUsrId")
									&& !this.fieldList.get(i).getObjectName()
											.equals("crtTm")
									&& !this.fieldList.get(i).getObjectName()
											.equals("mdfUsrId")
									&& !this.fieldList.get(i).getObjectName()
											.equals("mdfTm")) {
								String temp = this.generateModel(lines[j], i);
								s += temp + "\n";
							}
						} else {
							String newLoopString = lines[j];
							String newLoopBody = "";
							while (lines[++j].trim().startsWith(END_LOOP) == false
									|| lines[j].indexOf(newLoopString
											.substring(newLoopString
													.indexOf("begin") + 5)) < 0) {
								newLoopBody += lines[j] + "\n";
							}
							s += this.processLoopBody(newLoopBody, newLoopString);
						}
					}
				}
			}
		} else if (loopString.indexOf("begin_loop_field_xml%") >= 0) {
			for (int i = 0; i < this.fieldList.size(); i++) {
				for (int j = 0; j < lines.length; j++) {
					if (lines[j].indexOf(BEGIN_LOOP) < 0) {
						if (!this.fieldList.get(i).getObjectName().equals("id")) {
							String temp = this.generateModel(lines[j], i);
							s += temp + "\n";
						}
					} else {
						String newLoopString = lines[j];
						String newLoopBody = "";
						while (lines[++j].trim().startsWith(END_LOOP) == false
								|| lines[j].indexOf(newLoopString
										.substring(newLoopString
												.indexOf("begin") + 5)) < 0) {
							newLoopBody += lines[j] + "\n";
						}
						s += this.processLoopBody(newLoopBody, newLoopString);
					}
				}
			}
		}else if (loopString.indexOf("loop_key_field%") >= 0) {
			List<Field> tempList = this.fieldList;
			this.fieldList = this.table.getPrimaryKeyList();
			s = this.processLoopBody(loopBody, "%begin_loop_field%");
			this.fieldList = tempList;
		} else if (loopString.indexOf("begin_loop_field_string%") >= 0) {
			List<Field> tempList = this.fieldList;
			this.fieldList = this.table.getStringFieldList();
			s = this.processLoopBody(loopBody, "%begin_loop_field%");
			this.fieldList = tempList;
		} else if (loopString.indexOf("begin_loop_field_number%") >= 0) {
			List<Field> tempList = this.fieldList;
			this.fieldList = this.table.getNumberFieldList();
			s = this.processLoopBody(loopBody, "%begin_loop_field%");
			this.fieldList = tempList;
		} else if (loopString.indexOf("begin_loop_field_date%") >= 0) {
			List<Field> tempList = this.fieldList;
			this.fieldList = this.table.getDateFieldList();
			s = this.processLoopBody(loopBody, "%begin_loop_field%");
			this.fieldList = tempList;
		}
		return s;

	}

	public String getObjectName(String tableName) {
		String s = tableName.toLowerCase();
		//s = s.replace("sq_", "");
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '_') {
				s = s.substring(0, i) + (s.charAt(i + 1) + "").toUpperCase()
						+ s.substring(i + 2);
			}
		}
		return s;
	}

	private String generateModel(String s, int seq) {
		Field field = (Field) this.fieldList.get(seq);

		if (s.indexOf("%field_desc_name%") >= 0) {
			String temp = field.getFieldDesc();
			s = s.replaceAll("%field_desc_name%", (temp == null || temp
					.length() == 0) ? field.getFieldName() : temp);
		}

		if (s.indexOf("%field_name%") >= 0) {
			s = s.replaceAll("%field_name%", field.getFieldName());
		}

		if (s.indexOf("%field_type_eos_class%") >= 0) {
			String className = field.getDataType().getClassName();
			
			//System.out.println("代码生成获取DateType:" + className + " width:"+ field.getWidth() + " DecimalWidth:"+ field.getDecimalWidth());
			
			if ("Timestamp".equals(className))
				className = "Date";
			else if ("Clob".equals(className))
				className = "String";
			else if ("Blob".equals(className))
				className = "byte[]";
			else if ("Integer".equals(className))
				className = "Integer";
			else if ("Double".equals(className))
				className = "Double";
			s = s.replaceAll("%field_type_eos_class%", className);
		}

		if (s.indexOf("%field_object_name%") >= 0) {
			s = s.replaceAll("%field_object_name%", field.getObjectName());
		}
		
		if (s.indexOf("%field_object_name_get_set_name%") >= 0) {
			s = s.replaceAll("%field_object_name_get_set_name%", toUpperCaseFirst(field.getObjectName()));
		}

		if (s.indexOf("%field_type_class_hibernate%") >= 0) {
			s = s.replaceAll("%field_type_class_hibernate%",
					field.getColumnDataType());
		}

		return s;
	}
	
	public static String toUpperCaseFirst(String str){
		 StringBuffer sb = new StringBuffer(str);
		 sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
		 return sb.toString(); 
	}
	
	/**
	 * 将驼峰字符串转换为中划线小写显示
	 * @param str
	 * @return
	 */
	 @SuppressWarnings({ "rawtypes", "unchecked" })
	public static String transToMiddleline(String str){ 
	        List record =new ArrayList(); 
	        for(int i=0;i<str.length();i++) 
	        { 
	            char tmp =str.charAt(i); 
	            if((tmp<='Z')&&(tmp>='A')) 
	            { 
	                record.add(i);//记录每个大写字母的位置 
	            } 
	        } 
	        record.remove(0);//第一个不需加下划线 
	        str= str.toLowerCase(); 
	        char[] charofstr = str.toCharArray(); 
	        String[] t =new String[record.size()]; 
	        for(int i=0;i<record.size();i++) 
	        { 
	            t[i]="-"+charofstr[(int)record.get(i)];//加“-” 
	        } 
	        String result =""; 
	        int flag=0; 
	        for(int i=0;i<str.length();i++) 
	        { 
	            if((flag<record.size())&&(i==(int)record.get(flag))){ 
	                result+=t[flag]; 
	                flag++; 
	            } 
	            else 
	                result+=charofstr[i]; 
	        } 
	        return result; 
	  }
}
